<script lang="ts">
/**
 * author:hushangyue
 * create date: 2023.03.20
 */
export default {
    name: 'RelaxEditor'
};
</script>
<template>
    <div>
        <el-form v-if="fetch" ref="queryRef" :model="queryData" :inline="true"
            style="margin-left: 10%; margin-bottom: 30px">
            <el-form-item v-for="(item, key) in query" :key="key" style="margin-right: 5px">
                <el-date-picker v-if="item.type == 'daterange'" v-model="queryData[key]" type="daterange" unlink-panels
                    range-separator="To" start-placeholder="开始日期" end-placeholder="结束日期" :shortcuts="shortcuts"
                    :style="item.style || { width: (item.width || 260) + 'px' }" />
                <el-date-picker v-else-if="item.type == 'date' || item.type == 'datetime'" v-model="queryData[key]"
                    :type="item.type" class="filter-item" :placeholder="item.title || '选择日期'"
                    :style="item.style || { width: (item.width || 160) + 'px' }" />

                <el-select v-else-if="item.options" v-model="queryData[key]" :placeholder="item.title || '请选择'"
                    clearable :style="item.style || { width: (item.width || 160) + 'px' }" class="filter-item"
                    filterable>
                    <el-option v-for="option in getQueryOption(item.options)" :key="option.value" :label="option.label"
                        :value="option.value" />
                </el-select>
                <el-input v-else-if="item.type == 'number'" type="number" v-model="queryData[key]"
                    :placeholder="item.title" clearable :style="item.style || { width: (item.width || 140) + 'px' }"
                    class="filter-item" />
                <el-input v-else v-model="queryData[key]" :placeholder="item.title" clearable
                    :style="item.style || { width: (item.width || 140) + 'px' }" class="filter-item" />
            </el-form-item>

            <el-form-item>
                <el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
            </el-form-item>
        </el-form>

        <el-form v-if="form && bShow" ref="formRef" :model="formData">
            <el-form-item v-for="(item, key) in form" style="margin-right: 5px" :key="key" :label="item.title"
                label-width="140px" :required="item.required">
                <el-date-picker v-if="item.type == 'daterange'" v-model="formData[key]" type="daterange" unlink-panels
                    range-separator="To" start-placeholder="开始日期" end-placeholder="结束日期" :shortcuts="shortcuts" :style="item.style || {
                            width: (item.width || formWidth) + 'px'
                        }
                        " />
                <el-date-picker v-else-if="item.type == 'date' || item.type == 'datetime'" v-model="formData[key]"
                    :type="item.type" class="filter-item" :placeholder="item.title || '选择日期'" :style="item.style || {
                            width: (item.width || formWidth) + 'px'
                        }
                        " />

                <div v-else-if="item.options" :style="item.style || {
                        width: (item.width || formWidth) + 'px'
                    }
                    ">
                    <div v-if="item.type == 'checkbox'">
                        <span v-for="option in fitOptions(item.options)" :key="option.value">
                            <el-checkbox v-model="checkbox[key + '.' + option.value]" :label="option.label"
                                @change="onCheckboxChange(key, option.value)">
                            </el-checkbox>
                        </span>
                    </div>

                    <el-radio-group v-else-if="item.type == 'radio'" v-model="formData[key]">
                        <el-radio v-for="option in fitOptions(item.options)" :key="option.value"
                            :label="option.value">{{ option.label }}</el-radio>
                    </el-radio-group>

                    <el-select v-else v-model="formData[key]" :placeholder="item.placeholder || item.title || '请选择'
                        " clearable class="filter-item" filterable :style="item.style || {
                                width: (item.width || formWidth) + 'px'
                            }
                            ">
                        <el-option v-for="option in fitOptions(item.options)" :key="option.value" :label="option.label"
                            :value="option.value">
                        </el-option>
                    </el-select>
                </div>

                <el-switch v-else-if="item.type == 'switch'" v-model="formData[key]" :active-value="1"
                    :inactive-value="0"></el-switch>
                <el-input-number v-else-if="item.type == 'number'" type="number" v-model="formData[key]"
                    :step="item.step || 1" :placeholder="item.placeholder || item.title" clearable :style="item.style || {
                            width: (item.width || formWidth) + 'px'
                        }
                        " class="filter-item" />
                <el-input v-else-if="item.type == 'textarea'" type="textarea" v-model="formData[key]"
                    :autosize="{ minRows: item.height || 4 }" :placeholder="item.placeholder || item.title" clearable
                    :style="item.style || {
                            width: (item.width || formWidth) + 'px'
                        }
                        " :minLength="item.min" :maxLength="item.max" class="filter-item" />
                <el-input v-else v-model="formData[key]" :placeholder="item.placeholder || item.title" clearable
                    :show-password="!!item.password" :style="item.style || {
                            width: (item.width || formWidth) + 'px'
                        }
                        " class="filter-item" />
                <span>
                    <el-tooltip v-if="!!item.tooltip" :content="item.tooltip" style="margin-left: 10px"><span
                            style="color: blue">[?]</span></el-tooltip>
                    <el-tooltip v-if="item.readonly" content="只读字段"><span style="color: red">[R]</span></el-tooltip>
                </span>
            </el-form-item>

            <hr style="width: 400px; margin-left: 100px" />
            <el-form-item style="margin-left: 300px; margin-top: 25px">
                <el-button type="primary" @click="submitHandler">确定</el-button>
            </el-form-item>
        </el-form>
    </div>
</template>

<script lang="ts" setup>
import { Search, Document } from '@element-plus/icons-vue';
import {
    onMounted,
    reactive,
    ref,
    toRefs,
    onBeforeMount,
    defineExpose
} from 'vue';

import { ElMessage } from 'element-plus';

interface EditorProps {
    // 允许根据条件进行查询，以编辑查询出来的数据
    // 例如：玩家ID
    query?: {
        [key: string]: {
            title: string;
            type?: 'daterange' | 'date' | 'number' | 'datetime';
            style?: string;
            width?: number;
            options?: TOptions | Function; //选项中用
            format?: string; //日期用
            default?: any; //默认值
        };
    };
    //配合query使用
    fetch?: (query: any) => any;

    form?: {
        [key: string]: {
            title: string;
            placeholder?: string;
            type?:
            | 'daterange'
            | 'date'
            | 'datetime'
            | 'number'
            | 'checkbox'
            | 'textarea'
            | 'radio'
            | 'switch';

            style?: string;
            width?: number;
            options?: TOptions | Function; //选项中用
            format?: string; //日期用
            default?: any; //默认值
            min?: number;
            max?: number;
            step?: number;
            password?: boolean;
            height?: number;
            isArray?: boolean; //自动判断是否是数组，若是数组指定key为number
            tooltip?: string;
            readonly?: boolean;
            required?: boolean; //是否必填
        };
    };
    data?: any; //初始化数据，方式1指定数据对象
    onSubmit: (
        queryData: { [key: string]: any },
        changedData: any,
        oldData: any
    ) => void;

    formWidth?: number;
}

const props = withDefaults(defineProps<EditorProps>(), {
    form: undefined,
    data: undefined,
    query: undefined,
    onSubmit: () => {
        console.log('未指定提交处理函数');
    },

    // 网络请求函数
    fetch: undefined,
    formWidth: 300
});

const state = reactive({
    queryData: {} as any,
    formData: {} as any,
    checkbox: {} as any,
    bShow: !props.fetch
});
const { checkbox, formData, queryData, bShow } = toRefs(state);

// 快捷按钮
const shortcuts = [
    {
        text: 'Last week',
        value: () => {
            const end = new Date();
            const start = new Date();
            start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
            return [start, end];
        }
    },
    {
        text: 'Last month',
        value: () => {
            const end = new Date();
            const start = new Date();
            start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
            return [start, end];
        }
    },
    {
        text: 'Last 3 months',
        value: () => {
            const end = new Date();
            const start = new Date();
            start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
            return [start, end];
        }
    }
];

function getQueryOption(
    options: Function | TOptions
): Array<{ value: string | number; label: string }> {
    let opts: any = [];
    let temp: any = typeof options == 'function' ? options() : options;
    for (let k in temp) {
        let opt: any = temp[k];

        if (typeof opt == 'string' || typeof opt == 'number') {
            opts.push({ value: k, label: opt });
        } else {
            opts.push({ value: opt.value, label: opt.label });
        }
    }
    return opts;
}

function fitOptions(
    options: Function | TOptions
): Array<{ value: string | number; label: string }> {
    let opts: any = [];
    let temp: any = typeof options == 'function' ? options() : options;
    for (let k in temp) {
        let opt: any = temp[k];

        if (typeof opt == 'string' || typeof opt == 'number') {
            opts.push({ value: k.toString(), label: opt });
        } else {
            opts.push({ value: opt.value, label: opt.label });
        }
    }
    return opts;
}

function onCheckboxChange(key: string | number, value: string | number) {
    state.formData[key][value] = checkbox.value[key + '.' + value];
}

// 提出变更的数据
function pickChangedData(data: any, old: any) {
    if (!old) {
        return { changed: true, data };
    }

    let updata: any = {};
    let changed = 0;
    for (let k in data) {
        if (typeof data[k] == 'object') {
            if (JSON.stringify(data[k]) != JSON.stringify(old[k])) {
                updata[k] = data[k];
                changed++;
            }
        } else if (data[k] != old[k]) {
            updata[k] = data[k];
            changed++;
        }
    }
    return { changed, data: updata };
}
let oldData: any = null;
function submitHandler() {
    let data: any = {};
    for (let k in props.form) {
        let item = props.form[k];
        if (item.readonly) {
            continue;
        }
        if (item.type == 'switch') {
            data[k] = state.formData[k] ? 1 : 0;
        } else if (item.type == 'daterange') {
            if (state.formData[k]) {
                data[k] = [
                    state.formData[k][0].getTime(),
                    state.formData[k][1].getTime()
                ];
            }
        } else if (item.type == 'date' || item.type == 'datetime') {
            if (state.formData[k] && state.formData[k].getTime) {
                data[k] = state.formData[k].getTime();
            }
        } else if (item.options) {
            if (item.type == 'checkbox') {
                state.formData[k] = item.isArray ? [] : {};
                let opts = fitOptions(item.options!);
                for (let o in opts) {
                    data[k][opts[o].value] =
                        opts[o].value == state.formData[k][opts[o].value];
                }
            } else {
                if (item.isArray) {
                    data[k] = Number(state.formData[k]);
                } else {
                    data[k] = state.formData[k];
                }
            }
        } else {
            data[k] = state.formData[k];
        }
    }
    let updata = pickChangedData(data, oldData);
    if (updata.changed) {
        props.onSubmit(getQueryData(), updata.data, data);
    } else {
        ElMessage.error('没有数据变更');
    }
}

function getQueryData() {
    let query: any = {};
    if (state.queryData['id']) {
        query.id = state.queryData['id'];
    }
    if (props.query) {
        for (let k in props.query) {
            let item = props.query[k];

            if (item.type == 'daterange') {
                if (state.queryData[k]) {
                    query[k] = [
                        (state.queryData[k][0] as any).getTime(),
                        (state.queryData[k][1] as any).getTime()
                    ];
                }
            } else if (item.type == 'date' || item.type == 'datetime') {
                if (state.queryData[k] && state.queryData[k].getTime) {
                    query[k] = (state.queryData[k] as any).getTime();
                }
            } else {
                if (!state.queryData[k]) {
                    continue;
                }
                if (item.type == 'number') {
                    query[k] = parseInt(state.queryData[k]);
                } else {
                    query[k] = state.queryData[k];
                }
            }
        }
    }

    console.log('get query', query);
    return query;
}

function handleQuery() {
    if (props.fetch) {
        props.fetch(getQueryData()).then((result: any) => {
            setData(result.data);
        });
    }
}

//初始化数据，方式2 调用设置函数
function setData(data: any, dataId?: number | string) {
    if (!data) {
        ElMessage.error('没有数据');
        return;
    }
    state.bShow = true;
    oldData = data;
    if (dataId !== undefined) {
        state.queryData['id'] = dataId; //手动自定
    } else {
        state.queryData['id'] = data.id;
    }

    for (let k in props.form) {
        let item = props.form[k];
        if (data[k] === undefined) {
            continue;
        }
        if (item.type == 'switch') {
            state.formData[k] = data[k] ? 1 : 0;
        } else if (item.options) {
            if (item.type == 'checkbox') {
                let opts = fitOptions(item.options!);
                for (let o in opts) {
                    state.formData[k][opts[o].value] =
                        opts[o].value == data[k][opts[o].value];
                    checkbox.value[k + '.' + opts[o].value] =
                        opts[o].value == data[k][opts[o].value];
                }
            } else {
                state.formData[k] = data[k].toString();
            }
        } else {
            state.formData[k] = data[k];
        }
    }
}
// 需要配合下一帧使用，nextTick(()=>{editRef.value.setData(row); })
defineExpose({ setData });

onBeforeMount(() => {
    if (props.query) {
        for (let k in props.query) {
            let item = props.query[k];
            if (item.default !== undefined) {
                // state.query[k].value = item.default;
                state.queryData[k] = item.default;
            } else {
                state.queryData[k] = '';
            }
        }
    }

    if (props.form) {
        for (let k in props.form) {
            let item = props.form[k];

            if (item.type == 'switch') {
                state.formData[k] = item.default ? 1 : 0;
            } else if (item.options) {
                item.isArray = !!item.options.length;
                if (item.type == 'checkbox') {
                    state.formData[k] = {};
                    let opts = fitOptions(item.options!);
                    for (let o in opts) {
                        state.formData[k][opts[o].value] =
                            opts[o].value == item.default;
                        checkbox.value[k + '.' + opts[o].value] =
                            opts[o].value == item.default;
                    }
                } else if (item.type == 'radio') {
                    state.formData[k] = item.default || 0;
                } else {
                    state.formData[k] = item.default || '';
                }
            } else {
                if (item.default !== undefined) {
                    state.formData[k] = item.default;
                }
            }
        }

        if (props.data) {
            setData(props.data);
        }

        // if (!!props.fetch) {
        //     props.fetch(state.queryData).then((data: any) => {
        //         setData(data);
        //     });
        // }
    }
});
</script>

<style scoped>
.clickAble {
    color: blue;
    cursor: pointer;
}
</style>
